/*
 * Copyright (c) 2014-2020 杭州派迩信息技术有限公司 All Rights Reserved.
 * ProjectName: pi-flowable-project
 * ModuleName: pi-actuator-service
 * FileName: PiActuatorServiceInstanceServiceImpl.java
 * Author: kai.li
 * Date: 2021/08/31 18:16:31
 * Version: 1.0
 * LastModified: 2021/08/31 18:16:31
 */

package com.pisolution.actuator.service.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pisolution.actuator.service.constant.InstanceDetailType;
import com.pisolution.actuator.service.constant.ServiceInstantStatus;
import com.pisolution.actuator.service.exception.ActuatorServiceException;
import com.pisolution.actuator.service.mapper.PiActuatorServiceInstanceMapper;
import com.pisolution.actuator.service.model.PageActionResult;
import com.pisolution.actuator.service.model.PiActuatorServiceInstance;
import com.pisolution.actuator.service.model.ServiceInstanceReq;
import com.pisolution.actuator.service.schedule.HeartbeatMonitor;
import com.pisolution.actuator.service.service.PiActuatorServiceInstanceService;
import com.pisolution.actuator.service.util.HttpUtil;
import com.pisolution.actuator.service.util.IdUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @className: PiActuatorServiceInstanceServiceImpl
 * @description: 服务实现类
 * @author: kai.li
 * @date: 2021/08/31 18:16:31
 * @since: 1.0
 */

@Service
public class PiActuatorServiceInstanceServiceImpl implements PiActuatorServiceInstanceService {

    @Autowired
    private PiActuatorServiceInstanceMapper piActuatorServiceInstanceMapper;

    private final Map<String, HeartbeatMonitor> heartbeatMonitorMap = new ConcurrentHashMap<>();

    private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);

    private final static String END_POINT_REG = "^http[s]?://([\\d\\.]+):(\\d+)(/[\\w\\-]+)?$";

    //心跳监控任务延时（单位：秒）
    private final static long HEART_BEAT_MONITOR_DELAY = 20;

    /**
     * @title 服务注册
     * @description
     * @author admin
     * @updateTime 2021/9/2 10:09
     * @throws
     */
    @Transactional
    @Override
    public String register(Map<String, Object> registerInfo) {
        String name = (String)registerInfo.get("name");
        String endpoint = (String)registerInfo.get("endpoint");

        Long registerId = IdUtil.getInstance().getId();
        PiActuatorServiceInstance instance = new PiActuatorServiceInstance();
        instance.setId(registerId);
        instance.setInstanceName(name);
        instance.setRegisterTime(Instant.now());
        instance.setInstanceStatus(ServiceInstantStatus.UP.getCode());
        instance.setUpdateTime(Instant.now());

        //解析endpoint
        Pattern pattern = Pattern.compile(END_POINT_REG);
        Matcher matcher = pattern.matcher(endpoint);
        String ip ;
        String port ;
        String context ;
        if(matcher.matches()){
            ip = matcher.group(1);
            port = matcher.group(2);
            context = matcher.group(3);

            instance.setClientIp(ip);
            instance.setClientPort(port);
            instance.setClinetContext(context);
        }else{
            throw new ActuatorServiceException("endpoint解析失败:" + endpoint);
        }

        PiActuatorServiceInstance existInstant = piActuatorServiceInstanceMapper.selectByUniqKey(ip, port, context);
        if(existInstant == null){
            //启动心跳监控任务
            startHeartbeatMonitor(instance);

            //如果之前没有注册过，则新增
            piActuatorServiceInstanceMapper.insert(instance);
            return registerId.toString();
        }else{
            //启动心跳监控任务
            startHeartbeatMonitor(existInstant);

            //如果之前注册过，则更新状态和时间
            existInstant.setInstanceStatus(ServiceInstantStatus.UP.getCode());
            existInstant.setUpdateTime(Instant.now());
            piActuatorServiceInstanceMapper.updateById(existInstant);
            return existInstant.getId().toString();
        }
    }

    /**
     * @title 启动心跳监控任务
     * @description
     * @author admin
     * @updateTime 2021/9/3 10:14
     * @throws
     */
    private void startHeartbeatMonitor(PiActuatorServiceInstance instance) {
        HeartbeatMonitor monitor = new HeartbeatMonitor(scheduledExecutorService,
            piActuatorServiceInstanceMapper,
            instance.getId().toString(),
            instance.getInstanceStatus());
        monitor.resetHeartbeat(HEART_BEAT_MONITOR_DELAY);
        heartbeatMonitorMap.put(instance.getId().toString(), monitor);
    }

    /**
     * @title 心跳监控
     * @description
     * @author admin
     * @updateTime 2021/9/2 10:09
     * @throws
     */
    @Override
    public String heartbeat(Map<String, Object> heartbeatInfo) {
        String applicationId = (String)heartbeatInfo.get("applicationId");
        PiActuatorServiceInstance instance = piActuatorServiceInstanceMapper.selectById(Long.valueOf(applicationId));
        if(instance == null){
            throw new ActuatorServiceException("监控的实例不存在！");
        }
        instance.setUpdateTime(Instant.now());
        piActuatorServiceInstanceMapper.updateById(instance);

        HeartbeatMonitor monitor = heartbeatMonitorMap.get(instance.getId().toString());
        if(monitor != null){
            monitor.resetHeartbeat(HEART_BEAT_MONITOR_DELAY);
        }
        return instance.getId().toString();
    }

//    public static void main(String[] args) {
//        String end_reg = "^http[s]?://([\\d\\.]+):(\\d+)(/[\\w\\-]+)?$";
//        String endpoint = "http://192.168.199.120:9000/ground-service";
//        Pattern pattern = Pattern.compile(end_reg);
//        Matcher matcher = pattern.matcher(endpoint);
//        if(matcher.find()){
//            System.out.println(matcher.group(1));
//            System.out.println(matcher.group(2));
//            System.out.println(matcher.group(3));
//        }
//    }

    /**
     * @title 注销服务
     * @description
     * @author admin
     * @updateTime 2021/9/2 10:09
     * @throws
     */
    @Override
    public String deregister(String applicationId) {
        PiActuatorServiceInstance instance = piActuatorServiceInstanceMapper.selectById(Long.valueOf(applicationId));
        if(instance == null){
            throw new ActuatorServiceException("注销实例不存在！");
        }
        instance.setUpdateTime(Instant.now());
        instance.setInstanceStatus(ServiceInstantStatus.DOWN.getCode());
        piActuatorServiceInstanceMapper.updateById(instance);

        HeartbeatMonitor monitor = heartbeatMonitorMap.get(instance.getId().toString());
        if(monitor != null){
            monitor.cancel();
        }
        return instance.getId().toString();
    }

    /**
     * @title 分页检索服务实例列表
     * @description
     * @author admin
     * @updateTime 2021/9/15 10:34
     * @throws
     */
    @Override
    public PageActionResult<PiActuatorServiceInstance> pageList(ServiceInstanceReq serviceInstanceReq) {
        PageActionResult<PiActuatorServiceInstance> result = new PageActionResult<PiActuatorServiceInstance>(piActuatorServiceInstanceMapper.selectPageList(new Page<>(serviceInstanceReq.getPageNumber(), serviceInstanceReq.getPageSize()), serviceInstanceReq));
        return result;
    }

    /**
     * @title 获取health信息
     * @description
     * @author admin
     * @updateTime 2021/9/15 15:18
     * @throws
     */
    @Override
    public JSONObject gethealth(Long instanceId) {
        PiActuatorServiceInstance instance = piActuatorServiceInstanceMapper.selectById(instanceId);
        if(instance == null){
            throw new ActuatorServiceException("当前实例不存在！");
        }
        if(!ServiceInstantStatus.UP.getCode().equals(instance.getInstanceStatus())){
            throw new ActuatorServiceException("当前实例状态不在线！");
        }
        StringBuilder urlBuilder = new StringBuilder();
        urlBuilder.append("http://")
            .append(instance.getClientIp())
            .append(":").append(instance.getClientPort())
            .append(StringUtils.isEmpty(instance.getClinetContext()) ? "" : instance.getClinetContext())
            .append(InstanceDetailType.HEALTH_SUFFIX);
        return HttpUtil.buildRT().getForObject(urlBuilder.toString(), JSONObject.class);
    }

    /**
     * @title 获取info信息
     * @description
     * @author admin
     * @updateTime 2021/9/15 15:18
     * @throws
     */
    @Override
    public JSONObject getInfo(Long instanceId) {
        PiActuatorServiceInstance instance = piActuatorServiceInstanceMapper.selectById(instanceId);
        if(instance == null){
            throw new ActuatorServiceException("当前实例不存在！");
        }
        if(!ServiceInstantStatus.UP.getCode().equals(instance.getInstanceStatus())){
            throw new ActuatorServiceException("当前实例状态不在线！");
        }
        StringBuilder urlBuilder = new StringBuilder();
        urlBuilder.append("http://")
            .append(instance.getClientIp())
            .append(":").append(instance.getClientPort())
            .append(StringUtils.isEmpty(instance.getClinetContext()) ? "" : instance.getClinetContext())
            .append(InstanceDetailType.INFO_SUFFIX);
        return HttpUtil.buildRT().getForObject(urlBuilder.toString(), JSONObject.class);
    }


    /**
     * @title 获取info信息
     * @description
     * @author admin
     * @updateTime 2021/9/15 15:18
     * @throws
     */
    @Override
    public JSONObject getEnv(Long instanceId) {
        PiActuatorServiceInstance instance = piActuatorServiceInstanceMapper.selectById(instanceId);
        if(instance == null){
            throw new ActuatorServiceException("当前实例不存在！");
        }
        if(!ServiceInstantStatus.UP.getCode().equals(instance.getInstanceStatus())){
            throw new ActuatorServiceException("当前实例状态不在线！");
        }
        StringBuilder urlBuilder = new StringBuilder();
        urlBuilder.append("http://")
            .append(instance.getClientIp())
            .append(":").append(instance.getClientPort())
            .append(StringUtils.isEmpty(instance.getClinetContext()) ? "" : instance.getClinetContext())
            .append(InstanceDetailType.ENV_SUFFIX);
        return HttpUtil.buildRT().getForObject(urlBuilder.toString(), JSONObject.class);
    }


    /**
     * @title 获取info信息
     * @description
     * @author admin
     * @updateTime 2021/9/15 15:18
     * @throws
     */
    @Override
    public JSONObject getMetrics(Long instanceId, String detail) {
        PiActuatorServiceInstance instance = piActuatorServiceInstanceMapper.selectById(instanceId);
        if(instance == null){
            throw new ActuatorServiceException("当前实例不存在！");
        }
        if(!ServiceInstantStatus.UP.getCode().equals(instance.getInstanceStatus())){
            throw new ActuatorServiceException("当前实例状态不在线！");
        }
        StringBuilder urlBuilder = new StringBuilder();
        urlBuilder.append("http://")
            .append(instance.getClientIp())
            .append(":").append(instance.getClientPort())
            .append(StringUtils.isEmpty(instance.getClinetContext()) ? "" : instance.getClinetContext())
            .append(InstanceDetailType.METRICS_SUFFIX)
            .append(StringUtils.isEmpty(detail) ? "" : "/" + detail);
        return HttpUtil.buildRT().getForObject(urlBuilder.toString(), JSONObject.class);
    }

}
